package kernel_db.parser;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import common.entities.persistent.*;
import common.entities.abst.*;


public class Parser {
	
	private static Pattern patternMovie = Pattern.compile("[a-zA-Z_0-9:,.!' -]*");
	private static Pattern patternYearWithBracket = Pattern.compile("[(][0-9][0-9][0-9][0-9][)]");
	private static Pattern patternYear = Pattern.compile("[0-9][0-9][0-9][0-9]");
	private static Pattern patternWhiteSpaces = Pattern.compile("[ \t]*");
	private static Pattern patternNumbers = Pattern.compile("[0-9]*");
	private static Pattern patternRankDistribution = Pattern.compile("[0-9.]*");
	private static Pattern patternMovieRank = Pattern.compile("([1])?[0-9][.][0-9]");
	private static Pattern patternRomanNumbers = Pattern.compile("([(][IVX]*[)])?");		
	private static Pattern patternLetters = Pattern.compile("[a-zA-Z'. ]*");	
	private static Pattern patternNameSeperation = Pattern.compile(", ");	
	private static Pattern patternActorRole = Pattern.compile("\\[" + "[a-zA-Z_0-9:.!'/ -]*" + "\\]");
	private static Pattern patternActorVoice = Pattern.compile("([(]voice[)])?");
	private static Pattern patternParamName = Pattern.compile("[a-zA-Z_]*");	
	private static Pattern patternStringParam = Pattern.compile("[a-zA-Z0-9.:'/\\\\_ -]*");

	private static Pattern patternINIParamName = Pattern.compile(
			"[-]"					+
			patternParamName.pattern());
	private static Pattern patternINILine = Pattern.compile(
			patternINIParamName.pattern()	 +
			"[=]" + "([\"])?" 				 +
			patternStringParam.pattern()	 +
			"([\"])?");
	private static Pattern patternINIStringParam = Pattern.compile(
			"[\"]"							+
			patternStringParam.pattern()	+
			"[\"]");
	private static Pattern patternINIIntParam = Pattern.compile(
			patternNumbers.pattern());
	
	private static Pattern patternNameLine = Pattern.compile(
			patternLetters.pattern() 									+
			patternNameSeperation.pattern()								+
			patternLetters.pattern() 	+ patternWhiteSpaces.pattern() 	+ 
			patternRomanNumbers.pattern());
	private static Pattern patternMovieLine = Pattern.compile(	
			patternMovie.pattern() 				+ patternWhiteSpaces.pattern() + 
			patternYearWithBracket.pattern()	+ patternWhiteSpaces.pattern() +
			patternYear.pattern() 			   	+ patternWhiteSpaces.pattern());
	private static Pattern patternDirectorLine = Pattern.compile(	
			patternWhiteSpaces.pattern()		+
			patternMovie.pattern() 				+ patternWhiteSpaces.pattern() + 
			patternYearWithBracket.pattern()	+ patternWhiteSpaces.pattern());
	private static Pattern patternActorLine = Pattern.compile(	
			patternWhiteSpaces.pattern()					+
			patternMovie.pattern()							+ patternWhiteSpaces.pattern() + 
			patternYearWithBracket.pattern() 				+ patternWhiteSpaces.pattern() + 
			patternActorVoice.pattern()						+ patternWhiteSpaces.pattern() +
			patternActorRole.pattern()						+ patternWhiteSpaces.pattern() + 
			"([<]" + patternNumbers.pattern() + "[>])?" 	+ patternWhiteSpaces.pattern());
	private static Pattern patternCountryLine = Pattern.compile(	
			patternMovie.pattern() 				+ patternWhiteSpaces.pattern() + 
			patternYearWithBracket.pattern() 	+ patternWhiteSpaces.pattern() + 
			patternLetters.pattern()			+ patternWhiteSpaces.pattern());
	private static Pattern patternRatingLine = Pattern.compile(
			patternWhiteSpaces.pattern() 		+
			patternRankDistribution.pattern() 		+ patternWhiteSpaces.pattern() + 
			patternNumbers.pattern()			+ patternWhiteSpaces.pattern() +
			patternMovieRank.pattern() 		   		+ patternWhiteSpaces.pattern() +
			patternMovie.pattern() 				+ patternWhiteSpaces.pattern() +
			patternYearWithBracket.pattern() 	+ patternWhiteSpaces.pattern());
	private static Pattern patternGenreLine = Pattern.compile(	
			patternMovie.pattern() 				+ patternWhiteSpaces.pattern() + 
			patternYearWithBracket.pattern() 	+ patternWhiteSpaces.pattern() + 
			patternLetters.pattern()			+ patternWhiteSpaces.pattern());
	
	
	/**
	 * @return true if lineStr fits the pattern and false otherwise.
	 */
	private static Boolean doesLineFitPattern(String lineStr, Pattern linePattern) {
		try {
			if (linePattern == patternMovieLine || 
				linePattern == patternDirectorLine || linePattern == patternActorLine || 
				linePattern == patternCountryLine || linePattern == patternGenreLine) {
				lineStr = skipWhiteSpaces(lineStr);
					if ((lineStr.charAt(0) == '\"') ||
						 (lineStr.charAt(0) == ' ') ||
						 (lineStr.charAt(0) == '\t') ||
						 (lineStr.charAt(0) == '\n'))
								return false;
			}
		
			Matcher matcher;
			matcher = linePattern.matcher(lineStr);
			matcher.find();
			return (matcher.group().length() == lineStr.length());
		}
		catch (Exception e) {
			return false;
		}
	}
	/**
	 * @return a string(from startOffset to endOffset) from currentRestOfLine that fits the pattern.
	 */
	private static String getStringByPattern(String currentRestOfLine, Pattern pattern, int startOffset, int endOffset) {
		try {
			Matcher matcher;
			matcher = pattern.matcher(currentRestOfLine);
			matcher.find();
			return (matcher.group().substring(matcher.start()+startOffset, matcher.end()-endOffset));
		}
		catch (Exception e) {
			return "";
		} 
	}
	/**
	 * @return a string from currentRestOfLine that fits the pattern.
	 */
	private static String getStringByPattern(String currentRestOfLine, Pattern pattern) {
		return getStringByPattern(currentRestOfLine, pattern, 0, 0);
	}
	/**
	 * @return true if lineStr contains the pattern name and false otherwise
	 */
	private static Boolean doesLineContainsName(String lineStr) {
		try {
			if ((lineStr.charAt(0) == ' ') ||
				(lineStr.charAt(0) == '\t') ||
				(lineStr.charAt(0) == '\n'))
					 	return false;
			
			String name = getStringByPattern(lineStr, patternNameLine);
		
			return (name.length() > 0);
		}
		catch (Exception e) {
			return false;
		} 
		
	}
	/**
	 * @return a string from currentRestOfLine which doesn't include lastAttribute + offset
	 */
	private static String getRestOfLine(String currentRestOfLine, String lastAttribute, int startOffset) {
		try {
			return currentRestOfLine.substring(lastAttribute.length() + startOffset);
		} catch (Exception e) { return ""; }
	}
	/**
	 * @return a string from currentRestOfLine which doesn't include lastAttribute
	 */
	private static String getRestOfLine(String currentRestOfLine, String lastAttribute) {
		return getRestOfLine(currentRestOfLine, lastAttribute, 0);
	}
	/**
	 * @return a string from currenRestOfLine with no whitespace in the beginning
	 */
	private static String skipWhiteSpaces(String currenRestOfLine) {
		try {
			String whiteSpaces = getStringByPattern(currenRestOfLine, patternWhiteSpaces);
			return currenRestOfLine.substring(whiteSpaces.length());
		} catch (Exception e) { return ""; }
	}
	
	
	/**
	 * parse a line into an AbstPersonEntity class.
	 * @return the string line without the part which contains the name
	 */
	public static String parseNameFromLine(String line, AbstPersonEntity person) {
		try {
			if (doesLineContainsName(line)) {
				String temp, name, firstName, lastName, number = null;
				
				name = getStringByPattern(line, patternNameLine);
				line = getRestOfLine(line, name);
				line = skipWhiteSpaces(line);
				
				//get first and last name
				lastName = getStringByPattern(name, patternLetters);
				name = getRestOfLine(name, lastName);
				
				temp = getStringByPattern(name, patternNameSeperation);
				name = getRestOfLine(name, temp);
				
				firstName = getStringByPattern(name, patternLetters);
				name = getRestOfLine(name, firstName);
				
				// check if we have person number
				if (name.charAt(0) == '(') {
					firstName = firstName.substring(0, firstName.length()-1);	// deleting the ' '
					number = getStringByPattern(name, patternRomanNumbers);
					if (number.length() > 0)
						person.setPersonNumber(number);
				}
				
				person.setFirstName(firstName);
				person.setLastName(lastName);
				return line;
			}
			
			return null;
		} catch (Exception e) { return null; }
	}
	/**
	 * parse a line from the movies file DB into a Movie class.
	 * @return the string line without the part which contains the movie
	 */
	public static String parseMovieFromLine(String line, Movie movie) {
		try {
			String movieTitle, movieYear;
	
			movieTitle = getStringByPattern(line, patternMovie, 0, 1);
			line = getRestOfLine(line, movieTitle, 1);
			line = skipWhiteSpaces(line);
			
			movieYear = getStringByPattern(line, patternYearWithBracket, 1, 1);
			line = getRestOfLine(line, movieYear, 2);
			line = skipWhiteSpaces(line);
			
			movie.setValues(movieTitle, movieYear);
			return line;
		} catch (Exception e) { return ""; }
	}
	/**
	 * parse a line into a Movie class.
	 * @return true if the line fits the movies DB pattern and false otherwise
	 */
	public static Boolean parseMovieLine(String line, Movie movie) {
		try {
			if (doesLineFitPattern(line, patternMovieLine)) {
				parseMovieFromLine(line, movie);
				return true;
			}
	
			return false;
		} catch (Exception e) { return false; }
	}
	/**
	 * parse a line from the countries file DB into a Movie & Country classes.
	 * @return true if the line fits the countries DB pattern and false otherwise
	 */
	public static Boolean parseCountryLine(String line, Movie movie, Country country) {
		try {
			if (doesLineFitPattern(line, patternCountryLine)) {
				String  movieCountry;
				
				line = parseMovieFromLine(line, movie);
				
				movieCountry = getStringByPattern(line, patternLetters);
				
				country.setName(movieCountry);
				return true;
			}

			return false;
		} catch (Exception e) { return false; }
	}
	/**
	 * parse a line from the genres file DB into a Movie & Genre classes.
	 * @return true if the line fits the genres DB pattern and false otherwise
	 */
	public static Boolean parseGenreLine(String line, Movie movie, Genre genre) {
		try {
			if (doesLineFitPattern(line, patternGenreLine)) {
				String  movieGenre;
				
				line = parseMovieFromLine(line, movie);
				
				movieGenre = getStringByPattern(line, patternLetters);
				
				genre.setName(movieGenre);
				return true;
			}
	
			return false;
		} catch (Exception e) { return false; }
	}
	/**
	 * parse a line from the ratings file DB into a Movie class.
	 * @return true if the line fits the ratings DB pattern and false otherwise
	 */
	public static Boolean parseRatingLine(String line, Movie movie) {
		try {
			if (line.indexOf('"') != -1)
				return false;
			if (doesLineFitPattern(line, patternRatingLine)) {
				String temp, movieVotes, movieRank;
				
				line = skipWhiteSpaces(line);
				temp = getStringByPattern(line, patternRankDistribution);
				line = getRestOfLine(line, temp);
				line = skipWhiteSpaces(line);
	
				movieVotes = getStringByPattern(line, patternNumbers);
				line = getRestOfLine(line, movieVotes);
				line = skipWhiteSpaces(line);
				
				movieRank = getStringByPattern(line, patternMovieRank);
				line = getRestOfLine(line, movieRank);
				line = skipWhiteSpaces(line);
				
				parseMovieFromLine(line, movie);
	
				movie.setRank(movieRank);
				movie.setVotes(movieVotes);
				return true;
			}
	
			return false;
		} catch (Exception e) { return false; }
		
	}
	/**
	 * parse a line from the directors file DB into a Movie or Director class.
	 * @return  ParsedMovie if the line fits the movie name part in the directors DB pattern, 
	 * 			ParsedName if the line fits the director name part in the directors DB pattern and 
	 * 		    LineDoesntFitPattern otherwise
	 */
	public static parserReturnType parseDirectorLine(String line, Movie movie, Director director) {
		try {
			String tempLine = parseNameFromLine(line, director);
			if (tempLine != null)
				line = tempLine;
				
			if (doesLineFitPattern(line, patternDirectorLine)) {
				line = skipWhiteSpaces(line);
				parseMovieFromLine(line, movie);
	
				if (tempLine == null)
					return parserReturnType.ParsedMovie;
			}
			
			if (tempLine != null)
				return parserReturnType.ParsedName;
			else
				return parserReturnType.LineDoesntFitPattern;
		} catch (Exception e) { return parserReturnType.LineDoesntFitPattern; }
	}
	/**
	 * parse a line from the actors\actresses file DB into a Movie or Actor class.
	 * @return  ParsedMovie if the line fits the movie name part in the actors\actresses DB pattern, 
	 * 			ParsedName if the line fits the actors\actresses name part in the actors\actresses DB pattern and 
	 * 		    LineDoesntFitPattern otherwise
	 */
	public static parserReturnType parseActorLine(String line, Movie movie, Actor actor) {
		try {
			String tempLine = parseNameFromLine(line, actor);
			if (tempLine != null)
				line = tempLine;
				
			if (doesLineFitPattern(line, patternActorLine)) {
				line = skipWhiteSpaces(line);
				parseMovieFromLine(line, movie);
				
				if (tempLine == null)
					return parserReturnType.ParsedMovie;
			}
	
			if (tempLine != null)
				return parserReturnType.ParsedName;
			else
				return parserReturnType.LineDoesntFitPattern;
		} catch (Exception e) { return parserReturnType.LineDoesntFitPattern; }
	}

	
	/**
	 * parse a param name from the ini file.
	 * @return the param name if the line fits the param name pattern and null otherwise
	 */
	public static String parseINIParamName(String line) {
		try {
			if (doesLineFitPattern(line, patternINILine)) {
				String paramName;
				
				paramName = getStringByPattern(line, patternINIParamName);
				
				return paramName;
			}
	
			return null;
		} catch (Exception e) { return null; }
	}
	/**
	 * parse a string param from the ini file.
	 * @return the param if the it fits the param string pattern and "" otherwise
	 */
	public static String parseINIStringParam(String line) {
		try {
			String paramName;
			String param;
			
			paramName = getStringByPattern(line, patternINIParamName);
			line = getRestOfLine(line, paramName, 1);	//cuts the '='
			
			param = getStringByPattern(line, patternINIStringParam, 1, 1);	//cuts the '"' in the beginning and in the end
			
			return param;
	
		} catch (Exception e) { return ""; }
	}
	/**
	 * parse an int param from the ini file.
	 * @return the param if the it fits the int param pattern and -1 otherwise
	 */
	public static int parseINIIntParam(String line) {
		try {
			String paramName;
			String param;
			
			paramName = getStringByPattern(line, patternINIParamName);
			line = getRestOfLine(line, paramName, 1);
			
			param = getStringByPattern(line, patternINIIntParam);
			
			return Integer.parseInt(param);
	
		} catch (Exception e) { return -1; }
	}
	
}
	
